// 逻辑处理模块

/**
 * 在这里定义和用户相关的路由处理函数，供 /router/user.js 模块进行调用
 */

// 2.1导入数据库操作模块：
const db = require("../db/index");

// 3.1导入bcryptjs  密码加密
const bcrypt = require("bcryptjs");

// 4.3在 /router_handler/user.js 模块的头部区域，导入 jsonwebtoken 包：
const jwt = require("jsonwebtoken");

// 4.5导入创建的config.js 文件，并向外共享 加密 和 还原 Token 的 jwtSecretKey 字符串：
const config = require("../config");

// 注册用户的处理函数
exports.regUser = (req, res) => {
  // 1.检测表单数据是否合法
  //1.1接收表单数据
  const userinfo = req.body;
  // 1.2判断数据是否合法
  if (!userinfo.username || !userinfo.password) {
    // return res.send({ statuas: 1, message: "用户或者密码不能为空" });
    return res.cc("用户或者密码不能为空"); //优化使用 red.cc代码
  }

  // 2.检测用户名是否被占用
  // 2.2定义SQL语句
  const sqlStr = `select * from ev_users where username = ?`;
  // 2.3执行SQL语句 根据结果判定用户名是否被占用
  db.query(sqlStr, [userinfo.username], (err, results) => {
    // 执行SQL语句失败
    // if (err) return res.send({ status: 1, message: err.message });
    if (err) return res.cc(err); //优化代码

    // 用户名被占用 返回值是一个数组  如果大于等于1 就是用户名已存在
    if (results.length > 0) {
      // return res.send({ status: 1, message: "用户名已存在,请更换其他用户名" });
      return res.cc("用户名已存在,请更换其他用户名"); //优化代码
    }
  });

  // 3.对密码进行加密处理 为了保证密码的安全性，不建议在数据库以 明文 的形式保存用户密码，推荐对密码进行 加密存储
  // 使用 bcryptjs 对用户密码进行加无法被逆向破解 同一明文密码多次加密，得到的加密结果各不相同，保证了安全性
  // npm i bcryptjs@2.4.3运行如下命令，安装指定版本

  // 3.2在注册用户的处理函数中，确认用户名可用之后，调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法，对用户的密码进行加密处理：
  // 对用户的密码,进行 bcrype 加密，返回值是加密之后的密码字符串
  userinfo.password = bcrypt.hashSync(userinfo.password, 10); //默认长度是10

  // 4.插入新用户
  // 4.1定义插入用户的 SQL 语句：
  const sqlstr = `insert into ev_users set ?`;
  db.query(
    sqlstr,
    { username: userinfo.username, password: userinfo.password },
    (err, results) => {
      // 执行SQL语句失败
      // if (err) return res.send({ status: 1, message: err.message });
      if (err) return res.cc(err); //优化代码

      // SQL 语句执行成功，但影响行数不为 1
      if (results.affectedRows != 1) {
        // return res.send({ status: 1, message: "注册失败" });
        return res.cc("注册失败"); //优化代码
      }
      // 注册成功
      // res.send({ status: 0, message: "注册成功" });
      res.cc("注册成功", 0); //优化代码
    }
  );
};

// 5. 优化 res.send() 代码
// 在处理函数中，需要多次调用 res.send() 向客户端响应 处理失败 的结果，为了简化代码，可以手动封装一个 res.cc() 函数
// 在 app.js 中，所有路由之前，声明一个全局中间件，为 res 对象挂载一个 res.cc() 函数

// 6.优化表单数据验证
// 在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且,后端做为数据合法性验证的最后一个关口,在拦截非法数据方面，起到了至关重要的作用
// 单纯的使用 if...else... 的形式对数据合法性进行验证，效率低下、出错率高、维护性差
// 因此，推荐使用第三方数据验证模块，来降低出错率、提高验证的效率与可维护性，让后端程序员把更多的精力放在核心业务逻辑的处理上
// 6.1安装 @hapi/joi 包，为表单中携带的每个数据项，定义验证规则：
// 6.2安装 @escook/express-joi 中间件，来实现自动对表单数据进行验证的功能：
// 6.3新建 /schema/user.js 用户信息验证规则模块

// 登录的处理函数
exports.login = (req, res) => {
  // 1.检测表单数据是否合法
  // router.post("/login", expressJoi(reg_login_schema), userHandler.login); 在router的user.js  登录模块已解决

  // 2.根据用户名查询用户的数据
  const userinfo = req.body; //2.1接收表单数据
  const sqlStr = `select * from ev_users where username = ?`;
  // 2.3进入数据库 执行SQL语句,查询用户数据
  db.query(sqlStr, userinfo.username, (err, results) => {
    //执行SQL语句失败
    if (err) return res.cc(err);
    //执行SQL语句成功 ,但是查询到数据条数不等于 1 没有当前用户
    if (results.length !== 1) return res.cc("登陆失败!");

    // 3.判断用户输入的密码是否正确
    // 核心实现思路：调用 bcrypt.compareSync(用户提交的密码, 数据库中的密码) 方法比较密码是否一致
    // 返回值是布尔值（true 一致、false 不一致）
    const compareSync = bcrypt.compareSync(
      userinfo.password,
      results[0].password
    );
    // 如果对比的结果等于 false, 则证明用户输入的密码错误
    if (!compareSync) return res.cc("登陆失败!");

    // 4.生成 JWT 的 Token 字符串   敏感的数据不加密 密码和user_pic
    //核心注意点：在生成 Token 字符串的时候，一定要剔除 密码 和 头像 的值
    //通过 ES6 的高级语法，快速剔除 密码 和 头像 的值：
    const user = { ...results[0], password: "", user_pic: "" }; //...results[0] ES6 的高级语法  获取到整个对象 并把密码和头像重新赋值为空
    // res.send(user);

    // 4.2运行如下的命令，安装生成 Token 字符串的包：npm i jsonwebtoken@8.5.1
    // 在 /router_handler/user.js 模块的头部区域，导入 jsonwebtoken 包：

    // 4.3创建 config.js 文件，并向外共享 加密 和 还原 Token 的 jwtSecretKey 字符串：

    // 4.4将用户信息对象加密成 Token 字符串：
    const tokenStr = jwt.sign(user, config.jwtSecretKry, {
      //将user 密码和头像为空的对象  调用config里的属性 加密成Token字符串
      expiresIn: "10h", //token 有效时间为10小时
    });
    // 4.5将生成的 Token 字符串响应给客户端：
    res.send({
      status: 0,
      message: "登陆成功",
      // 为了方便客户端使用 Token，在服务器端直接拼接上 Bearer 的前缀
      token: "Bearer " + tokenStr, //此处有空格
    });

    // 4.6 配置解析 Token 的中间件 在index.js文件里
  });
};
